﻿using Swifter.Data.Sql;
using Swifter.Tools;
using System;
using System.Linq.Expressions;
using System.Reflection;

namespace Swifter.Data.Linq
{
    internal sealed partial class Visiter
    {
        private HashCache<ExpressionType, IAsInvoker> NodeTypeVisitables { get; }

        private HashCache<MethodInfo, IAsInvoker> CallVisitables { get; }

        private Visiter()
        {
            NodeTypeVisitables = new HashCache<ExpressionType, IAsInvoker>();
            CallVisitables = new HashCache<MethodInfo, IAsInvoker>();

            NodeTypeVisitables.DirectAdd(ExpressionType.Call, new CallInvokeImpl());
        }


        public void AddVisiter<TExpression, TResult>(ExpressionType nodeType, IVisitable<TExpression, TResult> visiter) where TExpression : Expression
        {
            NodeTypeVisitables.LockDirectAdd(nodeType, new AsInvokerImpl<TExpression, TResult>(visiter));
        }

        public void AddVisiter<TExpression, TResult>(MethodInfo call, IVisitable<TExpression, TResult> visiter) where TExpression : Expression
        {
            CallVisitables.LockDirectAdd(call, new AsInvokerImpl<TExpression, TResult>(visiter));
        }

        public void AddVisiter<TExpression, TResult>(ExpressionType nodeType, Func<Visiter, ScopeInfo, TExpression, TResult> func) where TExpression : Expression
        {
            AddVisiter(nodeType, new FuncVisiter<TExpression, TResult>(func));
        }

        public void AddVisiter<TExpression, TResult>(MethodInfo call, Func<Visiter, ScopeInfo, TExpression, TResult> func) where TExpression : Expression
        {
            AddVisiter(call, new FuncVisiter<TExpression, TResult>(func));
        }

        public TDesired ProcessCall<TDesired>(ScopeInfo scope, MethodCallExpression expression) where TDesired : class
        {
            foreach (var item in CallVisitables.GetValues(expression.Method))
            {
                if (item.Is(expression, typeof(TDesired)))
                {
                    var result = item.Process<TDesired>(this, scope, expression);

                    if (result != null)
                    {
                        return result;
                    }
                }
            }

            throw new NotSupportedException("没有匹配的处理方法。");
        }

        public TDesired Process<TDesired>(ScopeInfo scope, Expression expression) where TDesired : class
        {
            foreach (var item in NodeTypeVisitables.GetValues(expression.NodeType))
            {
                if (item.Is(expression, typeof(TDesired)))
                {
                    var result = item.Process<TDesired>(this, scope, expression);

                    if (result != null)
                    {
                        return result;
                    }
                }
            }

            throw new NotSupportedException("没有匹配的处理方法。");
        }



        public static object ComputeValue(Expression expression)
        {
            return Expression.Lambda<Func<object>>(Expression.Convert(expression, typeof(object))).Compile()();
        }

        public static Condition Condition(Comparisons comparison, IValue left, IValue right)
        {
            return new Condition(ConditionTypes.And, comparison, left, right);
        }

        public interface IVisitable<TExpression, TResult> where TExpression : Expression
        {
            TResult Process(Visiter visiter, ScopeInfo scope, TExpression expression);
        }

        private sealed class FuncVisiter<TExpression, TResult> : IVisitable<TExpression, TResult> where TExpression : Expression
        {
            public readonly Func<Visiter, ScopeInfo, TExpression, TResult> Func;

            public FuncVisiter(Func<Visiter, ScopeInfo, TExpression, TResult> func)
            {
                Func = func;
            }

            public TResult Process(Visiter visiter, ScopeInfo scope, TExpression expression) => Func(visiter, scope, expression);
        }

        private interface IAsInvoker
        {
            TDesired Process<TDesired>(Visiter visiter, ScopeInfo scope, Expression expression) where TDesired : class;

            bool Is(Expression expression, Type returnType);
        }

        private sealed class CallInvokeImpl : IAsInvoker
        {
            public bool Is(Expression expression, Type returnType) => expression is MethodCallExpression;

            public TDesired Process<TDesired>(Visiter visiter, ScopeInfo scope, Expression expression) where TDesired : class => visiter.ProcessCall<TDesired>(scope, (MethodCallExpression)expression);
        }

        private sealed class AsInvokerImpl<TExpression, TResult> : IAsInvoker where TExpression : Expression
        {
            public IVisitable<TExpression, TResult> Visiter { get; }

            public AsInvokerImpl(IVisitable<TExpression, TResult> visiter)
            {
                Visiter = visiter;
            }

            public TDesired Process<TDesired>(Visiter visiter, ScopeInfo scope, Expression expression) where TDesired : class => XConvert<TDesired>.Convert(Visiter.Process(visiter, scope, (TExpression)expression));

            public bool Is(Expression expression, Type returnType) =>  typeof(TExpression).IsInstanceOfType(expression)  && returnType.IsAssignableFrom(typeof(TResult));
        }
    }
}